using System;
using System.Drawing;
using System.IO;

namespace MetaViewAnisotropicDiffusion
{
	namespace Matrix
	{
		public class RGBIntMatrix 
		{
			public IntMatrix r, g, b;
			public int XDim
			{
				get{return this.R.XDim;}
			}

			public int YDim
			{
				get{return this.R.YDim;}
			}
			public IntMatrix R
			{
				get{return r;}
			}
			public IntMatrix G
			{
				get{return g;}
			}
			public IntMatrix B
			{
				get{return b;}
			}

			public RGBIntMatrix(int xdim, int ydim)
			{
				r = new IntMatrix(xdim, ydim);
				g = new IntMatrix(xdim, ydim);
				b = new IntMatrix(xdim, ydim);
			}
			public RGBIntMatrix(int xdim, int ydim, int init)
			{
				r = new IntMatrix(xdim, ydim, init);
				g = new IntMatrix(xdim, ydim, init);
				b = new IntMatrix(xdim, ydim, init);
			}

			public RGBIntMatrix Clone()
			{
				RGBIntMatrix clone = new RGBIntMatrix(this.XDim, this.YDim);
				clone.r = this.R.Clone();
				clone.g = this.G.Clone();
				clone.b = this.B.Clone();
				return clone;
			}
			public IntMatrix Intensity()
			{
				IntMatrix tmp = new IntMatrix(this.XDim, this.YDim);
				for(int j=0; j<this.YDim; j++)
				{
					for(int i=0; i<this.XDim; i++)
					{
						tmp.data[i,j] = (int)(255*(Color.FromArgb(this.R.data[i,j], this.G.data[i,j], this.B.data[i,j])).GetBrightness());
					}
				}
				return tmp;
			}

			
			public RGBDoubleMatrix HSB()
			{
				RGBDoubleMatrix tmp = new RGBDoubleMatrix(this.XDim, this.YDim);
				for(int i=0;i<tmp.XDim;i++)
				{
					for(int j=0;j<tmp.YDim;j++)
					{
						tmp.r.data[i,j] = Color.FromArgb(this.r.data[i,j], this.g.data[i,j], this.b.data[i,j]).GetHue();
						tmp.g.data[i,j] = Color.FromArgb(this.r.data[i,j], this.g.data[i,j], this.b.data[i,j]).GetSaturation();
						tmp.b.data[i,j] = Color.FromArgb(this.r.data[i,j], this.g.data[i,j], this.b.data[i,j]).GetBrightness();
					}
				}
				return tmp;
			}




			public static RGBIntMatrix ReadImage(Bitmap B)
			{
				Bitmap image = new Bitmap(B);
				RGBIntMatrix temp = new RGBIntMatrix(image.Width, image.Height);
				for(int i=0;i<temp.XDim;i++)
				{
					for(int j=0;j<temp.YDim;j++)
					{
						temp.R.data[i,j] = image.GetPixel(i,j).R;
						temp.G.data[i,j] = image.GetPixel(i,j).G;
						temp.B.data[i,j] = image.GetPixel(i,j).B;
					}
				}
				return temp;
			
			}

			public static RGBIntMatrix ReadBitmap(Bitmap image)
			{
				RGBIntMatrix temp = new RGBIntMatrix(image.Width, image.Height);
				for(int i=0;i<temp.XDim;i++)
				{
					for(int j=0;j<temp.YDim;j++)
					{
						temp.R.data[i,j] = image.GetPixel(i,j).R;
						temp.G.data[i,j] = image.GetPixel(i,j).G;
						temp.B.data[i,j] = image.GetPixel(i,j).B;
					}
				}
				return temp;
			
			}

			public void WriteRawImage(string filename)
			{
				FileStream strm;
				BinaryWriter bwriter;
				try
				{
					strm = new FileStream(filename, FileMode.Create, FileAccess.Write);
					bwriter = new BinaryWriter(strm);
				}
				catch(Exception e)
				{
					Console.WriteLine(e.Message);
					Console.WriteLine("Cannot open" + filename + "for writing");
					return;
				}
				for(int j=0; j<this.YDim; j++)
				{
					for(int i=0; i<this.XDim; i++)
					{
						bwriter.Write((byte)this.r.data[i,j]);
						bwriter.Write((byte)this.g.data[i,j]);
						bwriter.Write((byte)this.b.data[i,j]);
					}
				}
				bwriter.Close();
				strm.Close();
			}

			public void WriteRawGrayImage(string filename)
			{
				FileStream strm;
				BinaryWriter bwriter;
				try
				{
					strm = new FileStream(filename, FileMode.Create, FileAccess.Write);
					bwriter = new BinaryWriter(strm);
				}
				catch(Exception e)
				{
					Console.WriteLine(e.Message);
					Console.WriteLine("Cannot open" + filename + "for writing");
					return;
				}
				for(int j=0; j<this.YDim; j++)
				{
					for(int i=0; i<this.XDim; i++)
					{
						byte gray = (byte)(255*(Color.FromArgb(this.R.data[i,j], this.G.data[i,j], this.B.data[i,j])).GetBrightness());
						bwriter.Write(gray);
					}
				}
				bwriter.Close();
				strm.Close();
			}

			public void WriteImage(string filename)
			{
				Bitmap image = new Bitmap(this.XDim, this.YDim);

				for(int i=0;i<this.XDim;i++)
				{
					for(int j=0;j<this.YDim;j++)
					{
						image.SetPixel(i, j, Color.FromArgb(this.R.data[i,j], this.G.data[i,j], this.B.data[i,j]));
					}
				}
				
				image.Save(filename);

			}



			public Bitmap WriteBitmap()
			{
				Bitmap image = new Bitmap(this.XDim, this.YDim);

				for(int i=0;i<this.XDim;i++)
				{
					for(int j=0;j<this.YDim;j++)
					{
						image.SetPixel(i, j, Color.FromArgb(this.R.data[i,j], this.G.data[i,j], this.B.data[i,j]));
					}
				}
				
				return image;
			}

			public static implicit operator RGBDoubleMatrix(RGBIntMatrix intMat)
			{
				RGBDoubleMatrix result = new RGBDoubleMatrix(intMat.XDim, intMat.YDim);
				for(int i=0;i<intMat.XDim;i++)
				{
					for(int j=0;j<intMat.YDim;j++)
					{
						result.R.data[i,j] = intMat.R.data[i,j];
						result.G.data[i,j] = intMat.G.data[i,j];
						result.B.data[i,j] = intMat.B.data[i,j];
					}
				}
				return result;

			}

			public static implicit operator MultiValuedDoubleMatrix(RGBIntMatrix dMat)
			{
				MultiValuedDoubleMatrix result = new MultiValuedDoubleMatrix(dMat.XDim, dMat.YDim, 3);
				result.comp[0] = dMat.R;
				result.comp[1] = dMat.G;
				result.comp[2] = dMat.B;
				return result;

			}

		}
		
		public class RGBDoubleMatrix 
		{
			public DoubleMatrix r, g, b;
			public int XDim
			{
				get{return this.R.XDim;}
			}

			public int YDim
			{
				get{return this.R.YDim;}
			}
			public DoubleMatrix R
			{
				get{return r;}
			}
			public DoubleMatrix G
			{
				get{return g;}
			}
			public DoubleMatrix B
			{
				get{return b;}
			}

			public RGBDoubleMatrix(int xdim, int ydim)
			{
				r = new DoubleMatrix(xdim, ydim);
				g = new DoubleMatrix(xdim, ydim);
				b = new DoubleMatrix(xdim, ydim);
			}
			public RGBDoubleMatrix(int xdim, int ydim, double init)
			{
				r = new DoubleMatrix(xdim, ydim, init);
				g = new DoubleMatrix(xdim, ydim, init);
				b = new DoubleMatrix(xdim, ydim, init);
			}

			public RGBDoubleMatrix Clone()
			{
				RGBDoubleMatrix tmp = new RGBDoubleMatrix(this.XDim, this.YDim);
				tmp.r = this.r.Clone();
				tmp.g = this.g.Clone();
				tmp.b = this.b.Clone();
				return tmp;
			}
			
			public double Max()
			{
				double max = this.r.Max();

				double m = this.g.Max();
				max = max>m ? max : m;

				m = this.b.Max();
				max = max>m ? max : m;

				return max;
			}

			public double Min()
			{
				double min = this.r.Min();

				double m = this.g.Min();
				min = min<m ? min : m;

				m = this.b.Min();
				min = min<m ? min : m;

				return min;
			}

			public double Average()
			{
				double average = 0.0;
				for(int i=0;i<this.XDim;i++)
				{
					for(int j=0;j<this.YDim;j++)
					{
						average += this.r.data[i,j];
						average += this.g.data[i,j];
						average += this.b.data[i,j];
					}
				}
				average /= 3*this.XDim*this.YDim;
				return average;
			}


			public void ImageScale()
			{
				double max = this.Max();
				double min = this.Min();
				//init matrix to zero if it is a constnt matrix.
				if(max == min)
				{
					this.r.Initialize(0.0);
					this.g.Initialize(0.0);
					this.b.Initialize(0.0);
				}
				else
				{
					this.r.SubtractValue(min);
					this.r.DivideValue(max-min);
					this.r.MultiplyValue(255.0);

					this.g.SubtractValue(min);
					this.g.DivideValue(max-min);
					this.g.MultiplyValue(255.0);

					this.b.SubtractValue(min);
					this.b.DivideValue(max-min);
					this.b.MultiplyValue(255.0);
				}

			}


			public RGBDoubleMatrix Opponency()
			{
				RGBDoubleMatrix tmp = new RGBDoubleMatrix(this.XDim, this.YDim);
				tmp.r = (this.r + this.g + this.b)/3d;
				tmp.g = this.r - this.g;
				tmp.b = (this.r + this.g)/-2d + this.b;
				return tmp;
			}

			public RGBDoubleMatrix Opponency2RGB()
			{
				RGBDoubleMatrix tmp = new RGBDoubleMatrix(this.XDim, this.YDim);
				tmp.r = this.r + 0.5*this.g - (1/3d)*this.b;
				tmp.g = this.r - 0.5*this.g - (1/3d)*this.b;
				tmp.b = this.r + (2/3d)*this.b;
				return tmp;
			}

			


			public static RGBDoubleMatrix ReadImage(string filename)
			{
				Bitmap image = new Bitmap(filename);
				RGBDoubleMatrix temp = new RGBDoubleMatrix(image.Width, image.Height);
				for(int i=0;i<temp.XDim;i++)
				{
					for(int j=0;j<temp.YDim;j++)
					{
						temp.R.data[i,j] = image.GetPixel(i,j).R;
						temp.G.data[i,j] = image.GetPixel(i,j).G;
						temp.B.data[i,j] = image.GetPixel(i,j).B;
					}
				}
				return temp;
			
			}

			public static RGBDoubleMatrix ReadBitmap(Bitmap image)
			{
				RGBDoubleMatrix temp = new RGBDoubleMatrix(image.Width, image.Height);
				for(int i=0;i<temp.XDim;i++)
				{
					for(int j=0;j<temp.YDim;j++)
					{
						temp.R.data[i,j] = image.GetPixel(i,j).R;
						temp.G.data[i,j] = image.GetPixel(i,j).G;
						temp.B.data[i,j] = image.GetPixel(i,j).B;
					}
				}
				return temp;
			
			}

			public void WriteImage(string filename)
			{
				Bitmap image = new Bitmap(this.XDim, this.YDim);

				for(int i=0;i<this.XDim;i++)
				{
					for(int j=0;j<this.YDim;j++)
					{
						image.SetPixel(i, j, Color.FromArgb((int)this.R.data[i,j], (int)this.G.data[i,j], (int)this.B.data[i,j]));
					}
				}
				
				image.Save(filename);

			}


			public void WriteImageScaled(string filename)
			{
				Bitmap image = new Bitmap(this.XDim, this.YDim);
				RGBDoubleMatrix tmp = this.Clone();
				tmp.ImageScale();

				for(int i=0;i<this.XDim;i++)
				{
					for(int j=0;j<this.YDim;j++)
					{
						image.SetPixel(i, j, Color.FromArgb((int)tmp.R.data[i,j], (int)tmp.G.data[i,j], (int)tmp.B.data[i,j]));
					}
				}
				
				image.Save(filename);

			}

			public Bitmap WriteBitmap()
			{
				Bitmap image = new Bitmap(this.XDim, this.YDim);

				for(int i=0;i<this.XDim;i++)
				{
					for(int j=0;j<this.YDim;j++)
					{
						if(Double.IsNaN(this.R.data[i,j]) || Double.IsNaN(this.G.data[i,j]) || Double.IsNaN(this.B.data[i,j]))
						{
							image.SetPixel(i, j, Color.White);
							Console.WriteLine("Problem creating the bitmap: [{0},{1}]", i,j);
						}
						else
						{
							image.SetPixel(i, j, Color.FromArgb((int)this.R.data[i,j], (int)this.G.data[i,j], (int)this.B.data[i,j]));
						}
					}
				}
				
				return image;

			}

			public Bitmap WriteBitmapScaled()
			{
				Bitmap image = new Bitmap(this.XDim, this.YDim);
				RGBDoubleMatrix tmp = this.Clone();
				tmp.ImageScale();

				return tmp.WriteBitmap();

			}



			public static implicit operator MultiValuedDoubleMatrix(RGBDoubleMatrix dMat)
			{
				MultiValuedDoubleMatrix result = new MultiValuedDoubleMatrix(dMat.XDim, dMat.YDim, 3);
				result.comp[0] = dMat.R;
				result.comp[1] = dMat.G;
				result.comp[2] = dMat.B;
				return result;

			}

			public static explicit operator RGBIntMatrix(RGBDoubleMatrix dMat)
			{
				RGBIntMatrix result = new RGBIntMatrix(dMat.XDim, dMat.YDim);
				result.r = (IntMatrix)dMat.R;
				result.g = (IntMatrix)dMat.G;
				result.b = (IntMatrix)dMat.B;
				return result;

			}

		}


	}
}